// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.core.metadata.types;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.dengues.core.CorePlugin;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf 1 2006-09-29 17:06:40Z qiang.zhang $
 * 
 */
public class MetadataTypesManager {

    private static Logger logger = Logger.getLogger(MetadataTypesManager.class);

    private static final String DBTYPES_FILE = "dbtypes_mapping.xml"; //$NON-NLS-1$

    private static final String ATTRI_TYPE = "type"; //$NON-NLS-1$

    private static final String ATTRI_LENGTH = "defaultLength"; //$NON-NLS-1$

    private static final String ATTRI_PRECISION = "defaultPrecision"; //$NON-NLS-1$

    private static MetadataTypesManager instance = new MetadataTypesManager();

    private static List<String> dbTypesLabels = new ArrayList<String>();

    private static Map<String, JavaType> dbToJavaTypes = new HashMap<String, JavaType>();

    private static Map<String, String> dbDefaultLength = new HashMap<String, String>();

    private static Map<String, String> dbDefaultPrecision = new HashMap<String, String>();

    /**
     * qiang.zhang MetadataTypesManager constructor comment.
     */
    private MetadataTypesManager() {
        loadDbTypes();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "loadDbTypes".
     */
    private static void loadDbTypes() {
        dbTypesLabels = new ArrayList<String>();
        dbToJavaTypes = new HashMap<String, JavaType>();
        dbDefaultLength = new HashMap<String, String>();
        dbDefaultPrecision = new HashMap<String, String>();
        Path filePath = new Path(DBTYPES_FILE);

        Bundle b = Platform.getBundle(CorePlugin.PLUGIN_ID);
        URL url;
        try {
            if (b != null) {
                url = FileLocator.toFileURL(FileLocator.find(b, filePath, null));
                File dir = new File(url.getPath());
                if (dir.isFile()) {
                    loadMapping(dir);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "loadMapping".
     * 
     * @param file
     */
    private static void loadMapping(File file) {
        try {
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder analyser = documentBuilderFactory.newDocumentBuilder();
            Document document = analyser.parse(file);
            Node dbmsNode = document.getFirstChild();
            List<Node> childElementNodes = getChildElementNodes(dbmsNode);
            List<Node> dbTypes = getChildElementNodes(childElementNodes.get(0));
            for (Node node : dbTypes) {
                NamedNodeMap typeNodeAtttributes = node.getAttributes();
                String typeValue = typeNodeAtttributes.getNamedItem(ATTRI_TYPE).getNodeValue(); //$NON-NLS-1$
                Node namedItem = typeNodeAtttributes.getNamedItem(ATTRI_LENGTH);
                if (namedItem != null && !dbDefaultLength.containsKey(typeValue)) {
                    dbDefaultLength.put(typeValue, namedItem.getNodeValue());
                }
                namedItem = typeNodeAtttributes.getNamedItem(ATTRI_PRECISION);
                if (namedItem != null && !dbDefaultPrecision.containsKey(typeValue)) {
                    dbDefaultPrecision.put(typeValue, namedItem.getNodeValue());
                }
                if (!dbTypesLabels.contains(typeValue)) {
                    dbTypesLabels.add(typeValue);
                } else {
                    logger.warn(typeValue + " is Repeated.");
                }
            }
            List<Node> languages = getChildElementNodes(childElementNodes.get(1));
            List<Node> mappings = getChildElementNodes(languages.get(0));
            for (Node node : mappings) {
                NamedNodeMap typeNodeAtttributes = node.getAttributes();
                String typeValue = typeNodeAtttributes.getNamedItem(ATTRI_TYPE).getNodeValue(); //$NON-NLS-1$
                List<Node> childElementNodes2 = getChildElementNodes(node);
                if (childElementNodes2.size() > 0) {
                    Node denguesType = childElementNodes2.get(0);
                    String javaType = denguesType.getAttributes().getNamedItem(ATTRI_TYPE).getNodeValue(); //$NON-NLS-1$
                    if (!dbToJavaTypes.keySet().contains(typeValue)) {
                        dbToJavaTypes.put(typeValue, DenguesTypesManager.getJavaTypeFromDBType(javaType));
                    }
                }
            }
            Collections.sort(dbTypesLabels);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getChildElementNodes".
     * 
     * @param parentNode
     * @return
     */
    private static List<Node> getChildElementNodes(Node parentNode) {
        Node childNode = parentNode.getFirstChild();
        ArrayList<Node> list = new ArrayList<Node>();
        while (childNode != null) {
            if (childNode.getNodeType() == Node.ELEMENT_NODE) {
                list.add(childNode);
            }
            childNode = childNode.getNextSibling();
        }
        return list;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDefaultPrecision".
     * 
     * @param dbType
     * @return
     */
    public static Integer getDefaultPrecision(String dbType) {
        String javaType = dbDefaultPrecision.get(dbType);
        if (javaType != null) {
            return Integer.parseInt(javaType);
        }
        return 0;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDefaultLength".
     * 
     * @param dbType
     * @return
     */
    public static Integer getDefaultLength(String dbType) {
        String javaType = dbDefaultLength.get(dbType);
        if (javaType != null) {
            return Integer.parseInt(javaType);
        }
        return 0;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getJavaTypeName".
     * 
     * @param dbType
     * @return
     */
    public static String getJavaTypeName(String dbType) {
        JavaType javaType = dbToJavaTypes.get(dbType);
        if (javaType == null) {
            loadDbTypes();
            javaType = dbToJavaTypes.get(dbType);
        }
        // if (javaType.isPrimitive()) {
        // return javaType.getPrimitiveClass().getName();
        // } else {
        Class nullableClass = javaType.getNullableClass();
        if (javaType.isGenerateWithCanonicalName()) {
            return nullableClass.getCanonicalName();
        } else {
            return nullableClass.getSimpleName();
        }
        // }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "isPrimitive".
     * 
     * @param dbType
     * @return
     */
    public static boolean isPrimitive(String dbType) {
        JavaType javaType = dbToJavaTypes.get(dbType);
        if (javaType == null) {
            loadDbTypes();
            javaType = dbToJavaTypes.get(dbType);
        }
        return javaType.isPrimitive();
    }

    /**
     * Getter for instance.
     * 
     * @return the instance
     */
    public static MetadataTypesManager getInstance() {
        return instance;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDBTypesLabels".
     * 
     * @return
     */
    public String[] getDBTypesLabels() {
        if (dbTypesLabels.isEmpty()) {
            loadDbTypes();
        }
        return dbTypesLabels.toArray(new String[0]);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "indexOfDBType".
     * 
     * @param type
     * @return
     */
    public Integer indexOfDBType(String type) {
        if (dbTypesLabels.isEmpty()) {
            loadDbTypes();
        }
        int indexOf = dbTypesLabels.indexOf(type);
        if (indexOf < 0)
            return 0;
        return indexOf;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getJavaTypeFromDBType".
     * 
     * @param dbType
     * @return
     */
    public static JavaType getJavaTypeFromDBType(String dbType) {
        JavaType javaType = dbToJavaTypes.get(dbType);
        if (javaType == null) {
            loadDbTypes();
            javaType = dbToJavaTypes.get(dbType);
        }

        return javaType;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDefaultFromDBType".
     * 
     * @param dbType
     * @return
     */
    public static String getDefaultFromDBType(String dbType) {
        JavaType javaType = dbToJavaTypes.get(dbType);
        if (javaType == null) {
            loadDbTypes();
            javaType = dbToJavaTypes.get(dbType);
        }

        return DenguesTypesManager.getDefault(javaType);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDefaultDBTypeLabel".
     * 
     * @return
     */
    public String getDefaultDBTypeLabel() {
        return "VARCHAR"; //$NON-NLS-1$
    }
}
